module pwm (clk,

			rst,
			cs,
			rd,
			wr,

			write_addr,
			read_addr,
			data_in,
			data_out,

			pwmout_0,
			pwmout_1,
			pwmout_2,
			pwmout_3,

			irq
			);

input			  clk;

input			  rst;
input			  cs;
input			  rd;
input			  wr;

input		[7:0] write_addr;
input		[7:0] read_addr;
input 		[7:0] data_in;

output reg 	[7:0] data_out;
output   		  pwmout_0;
output   		  pwmout_1;
output   		  pwmout_2;
output   		  pwmout_3;
output	 		  irq;

reg			[7:0]	clk_con;			//clk configuration register	
reg			[1:0]	cnt_con;			//counter configuration register
reg			[4:0]	cp_con_0;		
reg			[4:0]	cp_con_1;
reg			[4:0]	cp_con_2;
reg			[4:0]	cp_con_3;		//comparator 0 to 3 configuration register
wire		[7:0]	cont_h;		//counter register high 8 bits
wire		[7:0]	cont_l;		//counter register low 8 bits
reg			[7:0]	comp0_h;
reg			[7:0]	comp0_l;
reg			[7:0]	comp1_h;
reg			[7:0]	comp1_l;
reg			[7:0]	comp2_h;
reg			[7:0]	comp2_l;
reg			[7:0]	comp3_h;
reg			[7:0]	comp3_l;		//comparator 0-3 register high & low bits
wire		[4:0]	int_flag;

reg 		[7:0]	snapshot;		//snapshot register for read pwmcont_h accurately
reg			[15:0]	cnt;			//counter register

reg					pwmout0reg;					
reg					pwmout1reg;
reg					pwmout2reg;
reg					pwmout3reg;
reg			[7:0]	autoreload0_h;
reg			[7:0]	autoreload0_l;
reg			[7:0]	autoreload1_h;
reg			[7:0]	autoreload1_l;
reg			[7:0]	autoreload2_h;
reg			[7:0]	autoreload2_l;
reg			[7:0]	autoreload3_h;
reg			[7:0]	autoreload3_l;
reg					cnt17;

wire				clk_en;
reg			[7:0]	clk_cnt;

//read register
always @(*)
begin
  if (cs & rd)
    case(read_addr)
  	  8'h0:		data_out = clk_con;
	  8'h1:		data_out = {cnt_con[1:0],6'b000000};
	  8'h2:	 	data_out = {cp_con_0[4:2],1'b0,cp_con_0[1:0],2'b00};
	  8'h3:		data_out = {cp_con_1[4:2],1'b0,cp_con_1[1:0],2'b00};
	  8'h4:		data_out = {cp_con_2[4:2],1'b0,cp_con_2[1:0],2'b00};
	  8'h5:		data_out = {cp_con_3[4:2],1'b0,cp_con_3[1:0],2'b00};
	  8'h6:		data_out = cont_h;
	  8'h7:		data_out = cont_l;
	  8'h8:		data_out = comp0_h;
	  8'h9:		data_out = comp0_l;
	  8'hA:		data_out = comp1_h;
	  8'hB:		data_out = comp1_l;
	  8'hC:		data_out = comp2_h;
	  8'hD:		data_out = comp2_l;
	  8'hE:		data_out = comp3_h;
	  8'hF:		data_out = comp3_l;
	  8'h10:	data_out = {int_flag[4:0],3'b000};
 	  default:	data_out = 8'd0; 
 	endcase
 else
 	data_out = 8'd0;
end

wire	read_cont_l = cs & rd & ( read_addr == 8'd7); 
wire	read_int_flag = cs & rd & (read_addr == 8'h10);

// clk_con cnt_con cp_con_0~3
always @(posedge clk, posedge rst)
begin
  if(rst)
  begin
    clk_con <= #1 8'hFF;
    cnt_con <= #1 2'b00;
    cp_con_0 <= #1 5'd0;
    cp_con_1 <= #1 5'd0;
    cp_con_2 <= #1 5'd0;
    cp_con_3 <= #1 5'd0;
	autoreload0_h <= #1 8'd0;
    autoreload0_l <= #1 8'd0;
    autoreload1_h <= #1 8'd0;
    autoreload1_l <= #1 8'd0;
    autoreload2_h <= #1 8'd0;
    autoreload2_l <= #1 8'd0;
    autoreload3_h <= #1 8'd0;
    autoreload3_l <= #1 8'd0;
  end
  else if (cs & wr) 
    case (write_addr)
  	  8'h0:	clk_con <= #1 data_in;
  	  8'h1:	cnt_con <= #1 {data_in[7],data_in[6]};
  	  8'h2:	cp_con_0<= #1 {data_in[7:5],data_in[3:2]};
  	  8'h3:	cp_con_1<= #1 {data_in[7:5],data_in[3:2]};
  	  8'h4:	cp_con_2<= #1 {data_in[7:5],data_in[3:2]};
  	  8'h5:	cp_con_3<= #1 {data_in[7:5],data_in[3:2]};
	  8'h8: autoreload0_h <= #1 data_in;
	  8'h9: autoreload0_l <= #1 data_in;
	  8'hA: autoreload1_h <= #1 data_in;
 	  8'hB: autoreload1_l <= #1 data_in;
	  8'hC: autoreload2_h <= #1 data_in;
	  8'hD: autoreload2_l <= #1 data_in;
	  8'hE: autoreload3_h <= #1 data_in;
 	  8'hF: autoreload3_l <= #1 data_in;
  	  default: ;
    endcase
end

//define control signal
wire	cr = cnt_con[1];		//cnt enable
wire    ecf = cnt_con[0];		//cnt overflow interrupt enable

wire [7:0] prer;
assign 	prer = clk_con;

wire	ecom_0   = cp_con_0[4];	//enable comparator
wire	pcent_0  = cp_con_0[3];	//Edge-aligned or center-aligned
wire	arsel_0  = cp_con_0[2];	//auto reload
wire	eccf_0   = cp_con_0[1];	//enable comparator interrupt 
wire	pwmpol_0 = cp_con_0[0];	//pwm output polarity

wire	ecom_1   = cp_con_1[4];	//enable comparator
wire	pcent_1  = cp_con_1[3];	//Edge-aligned or center-aligned
wire	arsel_1  = cp_con_1[2];	//auto reload
wire	eccf_1   = cp_con_1[1];	//enable comparator interrupt 
wire	pwmpol_1 = cp_con_1[0];	//pwm output polarity

wire	ecom_2   = cp_con_2[4];	//enable comparator
wire	pcent_2  = cp_con_2[3];	//Edge-aligned or center-aligned
wire	arsel_2  = cp_con_2[2];	//auto reload
wire	eccf_2   = cp_con_2[1];	//enable comparator interrupt 
wire	pwmpol_2 = cp_con_2[0];	//pwm output polarity

wire	ecom_3   = cp_con_3[4];	//enable comparator
wire	pcent_3  = cp_con_3[3];	//Edge-aligned or center-aligned
wire	arsel_3  = cp_con_3[2];	//auto reload
wire	eccf_3   = cp_con_3[1];	//enable comparator interrupt 
wire	pwmpol_3 = cp_con_3[0];	//pwm output polarity


//count_h & count_l and snapshow
//read PWMCONT low 8-bit first,then read high 8-bit
//high 8-bit will stash into a 'snapshot' register 
//when read low 8-bits data.
//read hight 8-bits will cause some error		

always @ (posedge clk , posedge rst)
begin
  if(rst)
  	snapshot <= #1 8'd0;
  else if (read_cont_l)
    snapshot <= #1 cnt[15:8];
end

assign cont_h = snapshot;
assign cont_l = cnt[7:0];

//comp0 with autoreload
wire write_comp0_h = cs & wr & (write_addr == 8'h8);
wire write_comp0_l = cs & wr & (write_addr == 8'h9);

always @ (posedge clk, posedge rst)
begin
  if (rst)
  begin
	comp0_h <= #1 8'h0;
	comp0_l <= #1 8'h0;
  end
  else if (~arsel_0 & write_comp0_h)		//when not autoreload
  	comp0_h <= data_in;
  else if (~arsel_0 & write_comp0_l)
   	comp0_l <= data_in;
  else if (arsel_0)
  begin
	if( (~pcent_0 & (cnt == 16'hFFFF)) | (pcent_0 & ({cnt17,cnt} == {1'b0,comp0_h,comp0_l}-17'b1)))
	begin
	  comp0_h <= autoreload0_h;
	  comp0_l <= autoreload0_l;
	end
  end
end

//comp1 with autoreload
wire write_comp1_h = cs & wr & (write_addr == 8'hA);
wire write_comp1_l = cs & wr & (write_addr == 8'hB);

always @ (posedge clk, posedge rst)
begin
  if (rst)
  begin
	comp1_h <= #1 8'h0;
	comp1_l <= #1 8'h0;
  end
  else if (~arsel_1 & write_comp1_h)		//when not autoreload
  	comp1_h <= data_in;
  else if (~arsel_1 & write_comp1_l)
   	comp1_l <= data_in;
  else if (arsel_1)
  begin
	if( (~pcent_1 & (cnt == 16'hFFFF)) | (pcent_1 & ({cnt17,cnt} == {1'b0,comp1_h,comp1_l}-17'b1)))
	begin
	  comp1_h <= autoreload1_h;
	  comp1_l <= autoreload1_l;
	end
  end
end

//comp2 with autoreload
wire write_comp2_h = cs & wr & (write_addr == 8'hC);
wire write_comp2_l = cs & wr & (write_addr == 8'hD);

always @ (posedge clk, posedge rst)
begin
  if (rst)
  begin
	comp2_h <= #1 8'h0;
	comp2_l <= #1 8'h0;
  end
  else if (~arsel_2 & write_comp2_h)		//when not autoreload
  	comp2_h <= data_in;
  else if (~arsel_2 & write_comp2_l)
   	comp2_l <= data_in;
  else if (arsel_2)
  begin
	if( (~pcent_2 & (cnt == 16'hFFFF)) | (pcent_2 & ({cnt17,cnt} == {1'b0,comp2_h,comp2_l}-17'b1)))
	begin
	  comp2_h <= autoreload2_h;
	  comp2_l <= autoreload2_l;
	end
  end
end

//comp3 with autoreload
wire write_comp3_h = cs & wr & (write_addr == 8'hE);
wire write_comp3_l = cs & wr & (write_addr == 8'hF);

always @ (posedge clk, posedge rst)
begin
  if (rst)
  begin
	comp3_h <= #1 8'h0;
	comp3_l <= #1 8'h0;
  end
  else if (~arsel_3 & write_comp3_h)		//when not autoreload
  	comp3_h <= data_in;
  else if (~arsel_3 & write_comp3_l)
   	comp3_l <= data_in;
  else if (arsel_3)
  begin
	if( (~pcent_3 & (cnt == 16'hFFFF)) | (pcent_3 & ({cnt17,cnt} == {1'b0,comp3_h,comp3_l}-17'b1)))
	begin
	  comp3_h <= autoreload3_h;
	  comp3_l <= autoreload3_l;
	end
  end
end

//generate clock enbale signal 
always @ (posedge clk, posedge rst)
begin
  if(rst)
  	clk_cnt <= #1 8'hFF;
  else if(!cr)
  	clk_cnt <= #1 prer;
  else if(cr & |clk_cnt )
  	clk_cnt <= #1 clk_cnt - 8'b1;
  else if (~|clk_cnt)
  	clk_cnt <= #1 prer;
end

assign clk_en = ~|clk_cnt;

//generate pwm cnt
always @(posedge clk, posedge rst)
begin
  if(rst)
	{cnt17,cnt}	<= #1 {1'b0,16'h0000};
  else if(!cr)
	{cnt17,cnt}	<= #1 {1'b0,16'h0000};
  else if( cr & clk_en )
  	{cnt17,cnt} <= #1 {cnt17,cnt} + 17'b1;
end


//output logic generation
//pwm output 0
always @ (posedge clk, posedge rst)
begin
  if(rst)
  	pwmout0reg <= #1 1;
  else if(~ecom_0 | ~cr)		//when disable (signal or overall)
  	pwmout0reg <= #1 1;
  else if (pcent_0)		//when center-aligned
    if({1'b0,comp0_h,comp0_l} <= {cnt17,cnt} && {cnt17,cnt} < ~{1'b0,comp0_h,comp0_l})
		pwmout0reg <= #1 ~pwmpol_0;
	else
		pwmout0reg <= #1 pwmpol_0;
  else if(~pcent_0)		//when edge-aligned
	if((cnt+16'b1) <= {comp0_h,comp0_l} )
		pwmout0reg <= #1 pwmpol_0;
	else
		pwmout0reg <= #1 ~pwmpol_0;
end

//pwm output 1
always @ (posedge clk, posedge rst)
begin
  if(rst)
  	pwmout1reg <= #1 1;
  else if(~ecom_1 | ~cr)		//when disable (signal or overall)
  	pwmout1reg <= #1 1;
  else if (pcent_1)		//when center-aligned
    if({1'b0,comp1_h,comp1_l} <= {cnt17,cnt} && {cnt17,cnt} < ~{1'b0,comp1_h,comp1_l})
		pwmout1reg <= #1 ~pwmpol_1;
	else
		pwmout1reg <= #1 pwmpol_1;
  else if(~pcent_1)		//when edge-aligned
	if((cnt+16'b1) <= {comp1_h,comp1_l} )
		pwmout1reg <= #1 pwmpol_1;
	else
		pwmout1reg <= #1 ~pwmpol_1;
end

//pwm output 2
always @ (posedge clk, posedge rst)
begin
  if(rst)
  	pwmout2reg <= #1 1;
  else if(~ecom_2 | ~cr)		//when disable (signal or overall)
  	pwmout2reg <= #1 1;
  else if (pcent_2)		//when center-aligned
    if({1'b0,comp2_h,comp2_l} <= {cnt17,cnt} && {cnt17,cnt} < ~{1'b0,comp2_h,comp2_l})
		pwmout2reg <= #1 ~pwmpol_2;
	else
		pwmout2reg <= #1 pwmpol_2;
  else if(~pcent_2)		//when edge-aligned
	if((cnt+16'b1) <= {comp2_h,comp2_l} )
		pwmout2reg <= #1 pwmpol_2;
	else
		pwmout2reg <= #1 ~pwmpol_2;
end

//pwm output 3
always @ (posedge clk, posedge rst)
begin
  if(rst)
    pwmout3reg <= #1 1;
  if(~ecom_3 | ~cr)		//when disable (signal or overall)
  	pwmout3reg <= #1 1;
  else if (pcent_3)		//when center-aligned
    if({1'b0,comp3_h,comp3_l} <= {cnt17,cnt} && {cnt17,cnt} < ~{1'b0,comp3_h,comp3_l})
		pwmout3reg <= #1 ~pwmpol_3;
	else
		pwmout3reg <= #1 pwmpol_3;
  else if(~pcent_3)		//when edge-aligned
	if((cnt+16'b1) <= {comp3_h,comp3_l} )
		pwmout3reg <= #1 pwmpol_3;
	else
		pwmout3reg <= #1 ~pwmpol_3;
end

assign pwmout_0 = pwmout0reg;
assign pwmout_1 = pwmout1reg;
assign pwmout_2 = pwmout2reg;
assign pwmout_3 = pwmout3reg;

//interrupt flag register
wire	cnt_overflow;
wire	comp0_match;
wire	comp1_match;
wire	comp2_match;
wire	comp3_match;
reg		cnt_overflow_q;
reg		comp0_match_q;
reg		comp1_match_q;
reg		comp2_match_q;
reg		comp3_match_q;

reg		cnt_overflow_flag;
reg		comp0_match_flag;
reg		comp1_match_flag;
reg		comp2_match_flag;
reg		comp3_match_flag;

assign cnt_overflow = cr & (cnt == 16'hFFFF);
assign comp0_match = cr & (cnt == {comp0_h,comp0_l});
assign comp1_match = cr & (cnt == {comp1_h,comp1_l});
assign comp2_match = cr & (cnt == {comp2_h,comp2_l});
assign comp3_match = cr & (cnt == {comp3_h,comp3_l});

//generate delay signal
always @(posedge clk, posedge rst)
begin
  if(rst)
  begin
  	cnt_overflow_q <= #1 1'b0;
	comp0_match_q <= #1 1'b0; 
	comp1_match_q <= #1 1'b0;
	comp2_match_q <= #1 1'b0;
	comp3_match_q <= #1 1'b0;
  end
  else 
  begin
  	cnt_overflow_q <= #1 cnt_overflow;
	comp1_match_q <= #1 comp0_match; 
	comp1_match_q <= #1 comp1_match; 
	comp2_match_q <= #1 comp2_match; 
	comp3_match_q <= #1 comp3_match; 
  end
end

always @(posedge clk, posedge rst)
begin
  if(rst)
    cnt_overflow_flag <= #1 1'b0;
  else if(cnt_overflow & ~cnt_overflow_q)	//trigger counter overflow interrupt
  	cnt_overflow_flag <= #1 1'b1;
  else if(!cr | read_int_flag)
    cnt_overflow_flag <= #1 1'b0;
end

always @(posedge clk, posedge rst)
begin
  if(rst)
    comp0_match_flag <= #1 1'b0;
  else if(comp0_match & ~comp0_match_q)	//trigger comp0 match overflow interrupt
  	comp0_match_flag <= #1 1'b1;
  else if(!cr | read_int_flag)
    comp0_match_flag <= #1 1'b0;
end

always @(posedge clk, posedge rst)
begin
  if(rst)
    comp1_match_flag <= #1 1'b0;
  else if(comp1_match & ~comp1_match_q)	//trigger comp1 match overflow interrupt
  	comp1_match_flag <= #1 1'b1;
  else if(!cr | read_int_flag)
    comp1_match_flag <= #1 1'b0;
end

always @(posedge clk, posedge rst)
begin
  if(rst)
    comp2_match_flag <= #1 1'b0;
  else if(comp2_match & ~comp2_match_q)	//trigger comp2 match overflow interrupt
  	comp2_match_flag <= #1 1'b1;
  else if(!cr | read_int_flag)
    comp2_match_flag <= #1 1'b0;
end

always @(posedge clk, posedge rst)
begin
  if(rst)
    comp3_match_flag <= #1 1'b0;
  else if(comp3_match & ~comp3_match_q)	//trigger comp3 match overflow interrupt
  	comp3_match_flag <= #1 1'b1;
  else if(!cr | read_int_flag)
    comp3_match_flag <= #1 1'b0;
end

assign int_flag = {cnt_overflow_flag,comp0_match_flag,comp1_match_flag,comp2_match_flag,comp3_match_flag};

assign	irq = (ecf & cnt_overflow_flag | eccf_0 & comp0_match_flag | eccf_1 & comp1_match_flag | eccf_2 & comp2_match_flag | eccf_3 & comp3_match_flag);

endmodule




